package com.foo.seam;

import java.beans.FeatureDescriptor;
import java.util.Iterator;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
import javax.el.ValueExpression;

import org.hibernate.validator.InvalidValue;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.el.EL;

/**
 * Caches instances of Hibernate Validator ClassValidator
 * 
 * @author Gavin King
 * @author Josh - patched up the inner class.
 */
@Name("org.jboss.seam.core.validators")
@BypassInterceptors
@Scope(ScopeType.APPLICATION)
public class Validators extends org.jboss.seam.core.Validators {

	/**
	 * Validate that the given value can be assigned to the property given by
	 * the value expression.
	 * 
	 * @param valueExpression
	 *            a value expression, referring to a property
	 * @param elContext
	 *            the ELContext in which to evaluate the expression
	 * @param value
	 *            a value to be assigned to the property
	 * @return a set of potential InvalidValues, from Hibernate Validator
	 */
	@Override
	public InvalidValue[] validate(final ValueExpression valueExpression,
			final ELContext elContext, final Object value) {
		final ValidatingResolver validatingResolver = new ValidatingResolver(
				elContext.getELResolver());
		final ELContext decoratedContext = EL.createELContext(elContext,
				validatingResolver);
		valueExpression.setValue(decoratedContext, value);
		return validatingResolver.getInvalidValues();
	}

	class ValidatingResolver extends ELResolver {
		private final ELResolver delegate;
		private InvalidValue[] invalidValues;

		public ValidatingResolver(final ELResolver delegate) {
			this.delegate = delegate;
			invalidValues = new InvalidValue[0]; // <== Initialize array.
		}

		public InvalidValue[] getInvalidValues() {
			return invalidValues;
		}

		@Override
		public Class<?> getCommonPropertyType(final ELContext context,
				final Object value) {
			return delegate.getCommonPropertyType(context, value);
		}

		@Override
		public Iterator<FeatureDescriptor> getFeatureDescriptors(
				final ELContext context, final Object value) {
			return delegate.getFeatureDescriptors(context, value);
		}

		@Override
		public Class<?> getType(final ELContext context, final Object x,
				final Object y) throws NullPointerException,
				PropertyNotFoundException, ELException {
			return delegate.getType(context, x, y);
		}

		@Override
		public Object getValue(final ELContext context, final Object base,
				final Object property) throws NullPointerException,
				PropertyNotFoundException, ELException {
			return delegate.getValue(context, base, property);
		}

		@Override
		public boolean isReadOnly(final ELContext context, final Object base,
				final Object property) throws NullPointerException,
				PropertyNotFoundException, ELException {
			return delegate.isReadOnly(context, base, property);
		}

		@Override
		public void setValue(final ELContext context, final Object base,
				final Object property, final Object value)
				throws NullPointerException, PropertyNotFoundException,
				PropertyNotWritableException, ELException {
			if (base != null && property != null) {
				context.setPropertyResolved(true);
				invalidValues = getValidator(base).getPotentialInvalidValues(
						property.toString(), value);
			}
		}
	}
}
